//+---------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation, 2007
//
// File: AsyncResult.cs
//
// Contents: Helpers for implementing Asynchronous Programming Model
//
// Created by: Andrew Lin (yunlin) July 22nd, 2008
//
//----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace Microsoft.Reputation.Services
{
    /// <summary>
    /// Basic implementation of IAsyncResult for internal use to represent an asynchronous call
    /// </summary>
    public class AsyncResult : IAsyncResult
    {
        private readonly AsyncCallback m_callback;
        private readonly object m_asyncState;

        private const int StatePending = 0;
        private const int StateCompletedSynchronously = 1;
        private const int StateCompletedAsynchronously = 2;
        private int m_completedState = StatePending;

        private System.Diagnostics.Stopwatch m_stopWatch;

        private Exception m_exception;

        // This may or may not get set depending on usage
        private ManualResetEvent m_asyncWaitHandle;

        public AsyncResult(AsyncCallback callback, object state)
        {
            m_callback = callback;
            m_asyncState = state;
            m_stopWatch = new System.Diagnostics.Stopwatch();
            m_stopWatch.Start();
        }

        public void SetComplete(Exception exception, Boolean completedSynchronously)
        {
            //
            // Time to execute this async call
            //            
            m_stopWatch.Stop();
            //
            // Passing null for exception means no error occurred. 
            // This is the common case
            //
            m_exception = exception;
            //
            // The m_CompletedState field MUST be set prior calling the callback
            //
            int previousState = Interlocked.Exchange(
                ref m_completedState,
                completedSynchronously ? StateCompletedSynchronously : StateCompletedAsynchronously);

            if (previousState != StatePending)
            {
                throw new InvalidOperationException("You can set a result only once");
            }
            //
            // If the event exists, set it
            //
            if (m_asyncWaitHandle != null)
            {
                m_asyncWaitHandle.Set();
            }
            //
            // If a callback method was set, call it
            //
            if (m_callback != null)
            {
                try
                {
                    m_callback(this);
                }
                catch (Exception ex)
                {
                    //
                    // If the external callback throws an exception, there is nothing much
                    // we can do about it other than tracing it.
                    //
                    if (m_exception == null)
                    {
                        m_exception = ex;
                    }
                }
            }
        }

        public void EndInvoke()
        {
            //
            // This method assumes that only 1 thread calls EndInvoke 
            // for this object
            //
            if (!IsCompleted)
            {
                //
                // If the operation isn't done, wait for it
                //
                AsyncWaitHandle.WaitOne();
                AsyncWaitHandle.Close();
                m_asyncWaitHandle = null;  // Allow early GC
            }
            //
            // Operation is done: if an exception occured, throw it
            //
            if (m_exception != null)
            {
                throw m_exception;
            }
        }

        public long StopWatchMilliSec
        {
            get
            {
                return m_stopWatch.ElapsedMilliseconds;
            }
        }

        public long StopWatchTicks
        {
            get
            {
                return m_stopWatch.ElapsedTicks;
            }
        }

        public Exception GetException()
        {
            return m_exception;
        }

        public bool HasException()
        {
            return m_exception != null;
        }

        public bool ExceptionAlreadyLogged()
        {
            return (m_exception is LoggedException);
        }

        #region IAsyncResult members
        public Object AsyncState
        {
            get
            {
                return m_asyncState;
            }
        }

        public Boolean CompletedSynchronously
        {
            get
            {
                return Thread.VolatileRead(ref m_completedState) == StateCompletedSynchronously;
            }
        }

        public WaitHandle AsyncWaitHandle
        {
            get
            {
                if (m_asyncWaitHandle == null)
                {
                    Boolean done = IsCompleted;
                    ManualResetEvent mre = new ManualResetEvent(done);
                    if (Interlocked.CompareExchange(ref m_asyncWaitHandle, mre, null) != null)
                    {
                        // Another thread created this object's event; dispose 
                        // the event we just created
                        mre.Close();
                    }
                    else
                    {
                        if (!done && IsCompleted)
                        {
                            // If the operation wasn't done when we created 
                            // the event but now it is done, set the event
                            m_asyncWaitHandle.Set();
                        }
                    }
                }
                return m_asyncWaitHandle;
            }
        }

        public Boolean IsCompleted
        {
            get
            {
                return Thread.VolatileRead(ref m_completedState) != StatePending;
            }
        }
        #endregion
    }

    /// <summary>
    /// AsyncResult that represents an asynchronous call with given result type
    /// </summary>
    /// <typeparam name="TResult">Type of the result of an asynchronous call represented by this object</typeparam>
    public class AsyncResult<TResult> : AsyncResult
    {
        private TResult m_result = default(TResult);

        public AsyncResult(AsyncCallback callback, object state)
            : base(callback, state)
        {
        }

        public void SetComplete(TResult result, Boolean completedSynchronously)
        {
            m_result = result;

            // operation completed sucessfully (no exception)
            base.SetComplete(null, completedSynchronously);
        }

        new public TResult EndInvoke()
        {
            base.EndInvoke();
            // Return the result (if above didn't throw)
            return m_result;
        }
    }

    /// <summary>
    /// AsyncResult that represents an asynchronous call with a result and its own internal state, as opposed
    /// to just having the state passed by host that you don't control.
    /// </summary>
    /// <typeparam name="TResult">Type of the result of an asynchronous call represented by this object</typeparam>
    /// <typeparam name="TState">Type of the state to hold internally, regardless of state passed by host</typeparam>
    public class AsyncResultOwnState<TResult, TState> : AsyncResult<TResult>
    {
        private readonly TState m_ownState;

        public AsyncResultOwnState(TState ownState, AsyncCallback hostCallback, object hostState)
            : base(hostCallback, hostState)
        {
            m_ownState = ownState;
        }

        public TState OwnState
        {
            get
            {
                return m_ownState;
            }
        }
    }
}
